/*
 * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package com.xuexiang.xui_lib.component.progress.loading;

import ohos.agp.components.AttrHelper;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.element.PixelMapElement;
import ohos.agp.render.Arc;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.PixelMapHolder;
import ohos.agp.render.Shader;
import ohos.agp.render.SweepShader;
import ohos.agp.utils.Color;
import ohos.agp.utils.Matrix;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;
import ohos.eventhandler.EventRunner;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.media.image.ImageSource;
import ohos.media.image.PixelMap;
import ohos.media.image.common.PixelFormat;
import ohos.media.image.common.Rect;
import ohos.media.image.common.Size;

import com.xuexiang.xui_lib.ResourceTable;
import com.xuexiang.xui_lib.component.textview.marqueen.WeakHandler;

import java.io.IOException;
import java.io.InputStream;

/**
 * 圆弧旋转loading
 *
 * @author xuexiang
 * @since 2019/1/11 下午3:59
 */
public class ARCLoadingView extends Component {
    /**
     * 默认圆弧的宽度
     */
    private static final int DEFAULT_ARC_WIDTH = 6;
    /**
     * 默认旋转度数的速度
     */
    private static final int DEFAULT_SPEED_OF_DEGREE = 5;
    /**
     * 默认圆弧的角度
     */
    private static final int DEFAULT_ARC_DEGREE = 315;

    /**
     * 是否需要画图
     */
    private boolean mIsDraw = true;
    /**
     * 是否是自动模式，无需人工干预
     */
    private boolean mIsAutoMode = true;

    /**
     * 圆弧画笔
     */
    private Paint mArcPaint;
    /**
     * 圆弧画笔的粗细
     */
    private int mArcWidth;
    /**
     * 圆弧的颜色
     */
    private Color mArcColor;

    /**
     * 圆弧所在的椭圆对象
     */
    private RectFloat mArcRectF;
    /**
     * 圆弧开始的角度
     */
    private int mStartDegree = 10;
    /**
     * 圆弧的角度
     */
    private float mArcDegree;
    /**
     * 圆弧旋转角度的速度
     */
    private int mSpeedOfDegree;

    /**
     * 图标
     */
    private PixelMap pixelMap;
    /**
     * 像素图片持有者
     */
    private PixelMapHolder pixelMapHolder;

    /**
     * 图标缩放比例
     */
    private float mIconScale;
    /**
     * 绘制图标的画笔
     */
    private Paint mIconPaint;
    /**
     * 渐变色
     */
    private Color[] mGradientColors;

    private EventRunner runner = EventRunner.current();
    private WeakHandler mHandler;
    private long invalidateTime = 30;

    public ARCLoadingView(Context context) throws NotExistException, WrongTypeException, IOException {
        super(context);
        initAttrs(context, null);
    }

    public ARCLoadingView(Context context, AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        super(context, attrs);
        initAttrs(context, attrs);
    }

    public ARCLoadingView(Context context, AttrSet attrs, int defStyleAttr)
        throws NotExistException, WrongTypeException, IOException {
        super(context, attrs, defStyleAttr);
        initAttrs(context, attrs);
    }

    private void initAttrs(Context context, AttrSet attrs) throws NotExistException, WrongTypeException, IOException {
        if (attrs != null) {
            if (attrs.getAttr("lv_auto").isPresent()) {
                mIsDraw = mIsAutoMode = attrs.getAttr("lv_auto").get().getBoolValue();
            } else {
                mIsDraw = mIsAutoMode = true;
            }
            if (attrs.getAttr("lv_color").isPresent()) {
                mArcColor = attrs.getAttr("lv_color").get().getColorValue();
            } else {
                mArcColor = new Color(context.getColor(ResourceTable.Color_blue_light));
            }
            if (attrs.getAttr("lv_arc_degree").isPresent()) {
                mArcDegree = attrs.getAttr("lv_arc_degree").get().getFloatValue();
            } else {
                mArcDegree = DEFAULT_ARC_DEGREE;
            }
            if (attrs.getAttr("lv_width").isPresent()) {
                mArcWidth = attrs.getAttr("lv_width").get().getIntegerValue();
            } else {
                mArcWidth = DEFAULT_ARC_WIDTH;
            }
            if (attrs.getAttr("lv_speed").isPresent()) {
                mSpeedOfDegree = attrs.getAttr("lv_speed").get().getIntegerValue();
            } else {
                mSpeedOfDegree = DEFAULT_SPEED_OF_DEGREE;
            }
            boolean hasIcon;
            if (attrs.getAttr("lv_has_icon").isPresent()) {
                hasIcon = attrs.getAttr("lv_has_icon").get().getBoolValue();
            } else {
                hasIcon = true;
            }
            if (hasIcon) {
                if (attrs.getAttr("lv_icon").isPresent()) {
                    pixelMap = ((PixelMapElement) attrs.getAttr("lv_icon").get().getElement()).getPixelMap();
                } else {
                    pixelMap = getPixelMap(ResourceTable.Media_ic_launcher);
                }
                if (attrs.getAttr("lv_icon_scale").isPresent()) {
                    mIconScale = attrs.getAttr("lv_icon_scale").get().getFloatValue();
                } else {
                    mIconScale = 0.5F;
                }
            }
        } else {
            mArcColor = new Color(context.getColor(ResourceTable.Color_blue_light));
            mArcDegree = DEFAULT_ARC_DEGREE;
            mArcWidth = DEFAULT_ARC_WIDTH;
            mSpeedOfDegree = DEFAULT_SPEED_OF_DEGREE;
            pixelMap = getPixelMap(ResourceTable.Media_ic_launcher);
            mIconScale = 0.5F;
            mIsDraw = mIsAutoMode = true;
        }
        mGradientColors = new Color[]{Color.TRANSPARENT, mArcColor};
        if (pixelMap != null) {
            pixelMapHolder = new PixelMapHolder(pixelMap);
        }
        initPaint();
        initListener();
    }

    private PixelMap getPixelMap(int drawableId) {
        InputStream drawableInputStream = null;
        try {
            drawableInputStream = getContext().getResourceManager().getResource(drawableId);

            ImageSource.SourceOptions sourceOptions = new ImageSource.SourceOptions();
            sourceOptions.formatHint = "image/png";

            ImageSource imageSource = ImageSource.create(drawableInputStream, sourceOptions);
            ImageSource.DecodingOptions decodingOptions = new ImageSource.DecodingOptions();

            decodingOptions.desiredSize = new Size(0, 0);
            decodingOptions.desiredRegion = new Rect(0, 0, 0, 0);
            decodingOptions.desiredPixelFormat = PixelFormat.ARGB_8888;

            return imageSource.createPixelmap(decodingOptions);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (drawableInputStream != null) {
                    drawableInputStream.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    private void initListener() {
        setEstimateSizeListener(new EstimateSizeListener() {
            @Override
            public boolean onEstimateSize(int widthEstimateConfig, int heightEstimateConfig) {
                // 通过回去布局方式自适应宽高
                int width = EstimateSpec.getSize(widthEstimateConfig);
                int height = EstimateSpec.getSize(heightEstimateConfig);

                setEstimatedSize(
                    EstimateSpec.getChildSizeWithMode(width, width, EstimateSpec.NOT_EXCEED),
                    EstimateSpec.getChildSizeWithMode(height, height, EstimateSpec.NOT_EXCEED)
                );
                mArcRectF = new RectFloat(2 * mArcWidth,
                    2 * mArcWidth,
                    width - 2 * mArcWidth,
                    height - 2 * mArcWidth);
                return true;
            }
        });
        addDrawTask(new DrawTask() {
            @Override
            public void onDraw(Component component, Canvas canvas) {
                if (!mIsDraw) {
                    return;
                }
                drawArc(canvas);
            }
        });

        mHandler = new WeakHandler(runner);
        mHandler.post(runnable);
    }

    /**
     * 初始化画笔
     */
    private void initPaint() {
        mArcPaint = new Paint();
        mArcPaint.setColor(Color.RED);
        mArcPaint.setAntiAlias(true);
        mArcPaint.setStyle(Paint.Style.STROKE_STYLE);
        mArcPaint.setStrokeWidth(AttrHelper.vp2px(mArcWidth, getContext()));

        mIconPaint = new Paint();
        mIconPaint.setColor(Color.RED);
        mIconPaint.setAntiAlias(true);
        mIconPaint.setFilterBitmap(true);
        mIconPaint.setStyle(Paint.Style.STROKE_STYLE);
        mIconPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
    }

    /**
     * 画圆弧
     *
     * @param canvas
     */
    private void drawArc(Canvas canvas) {
        if (pixelMapHolder != null) {
            canvas.drawPixelMapHolderRect(pixelMapHolder, getIconBitmapRectF(pixelMapHolder), mIconPaint);
        }
        mArcPaint.setShader(getSweepGradient(canvas), Paint.ShaderType.SWEEP_SHADER);
        Arc arc = new Arc();
        arc.setArc(mStartDegree, mArcDegree, false);
        canvas.drawArc(mArcRectF, arc, mArcPaint);
        mStartDegree += mSpeedOfDegree;
        if (mStartDegree > 360) {
            mStartDegree = mStartDegree - 360;
        }
    }

    /**
     * 获取中心图标所在的区域
     *
     * @param pixelMapHolder
     * @return RectFloat
     */
    public RectFloat getIconBitmapRectF(PixelMapHolder pixelMapHolder) {
        float width = pixelMapHolder.getPixelMap().getImageInfo().size.width;
        float height = pixelMapHolder.getPixelMap().getImageInfo().size.height;
        if (width >= height) {
            height = getWidth() / width * height;
            width = getWidth();
        } else {
            width = getHeight() / height * width;
            height = getHeight();
        }
        float left = (getWidth() - width * mIconScale) / 2;
        float top = (getHeight() - height * mIconScale) / 2;
        float right = getWidth() - left;
        float bottom = getHeight() - top;
        return new RectFloat(left, top, right, bottom);
    }

    private Shader getSweepGradient(Canvas canvas) {
        SweepShader sweepGradient = new SweepShader(getWidth() >> 1, getHeight() >> 1, mGradientColors, null);
        Matrix matrix = new Matrix();
        matrix.setRotate(mStartDegree, getWidth() >> 1, getHeight() >> 1);
        sweepGradient.setShaderMatrix(matrix);
        return sweepGradient;
    }

    /**
     * 设置loading的图标
     *
     * @param icon
     * @return ARCLoadingView
     */
    public ARCLoadingView setLoadingIcon(PixelMap icon) {
        if (icon != null) {
            pixelMap = icon;
            pixelMapHolder = new PixelMapHolder(pixelMap);
        }
        return this;
    }

    public ARCLoadingView setLoadingIcon(int suorceId) {
        pixelMap = getPixelMap(suorceId);
        pixelMapHolder = new PixelMapHolder(pixelMap);
        return this;
    }

    /**
     * 设置loading的图标
     *
     * @param color a
     * @return ARCLoadingView
     */
    public ARCLoadingView setLoadingColor(Color color) {
        mArcColor = color;
        return this;
    }

    public Color getLoadingColor() {
        return mArcColor;
    }

    public ARCLoadingView setSpeedOfDegree(int speedOfDegree) {
        mSpeedOfDegree = speedOfDegree;
        return this;
    }

    /**
     * 设置图标的缩小比例
     *
     * @param iconScale
     * @return ARCLoadingView
     */
    public ARCLoadingView setIconScale(float iconScale) {
        mIconScale = iconScale;
        return this;
    }

    public float getIconScale() {
        return mIconScale;
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            mHandler.postDelayed(this, invalidateTime);
            invalidate();
        }
    };

    /**
     * 开始旋转
     */
    public void start() {
        if (!mIsAutoMode) {
            mIsDraw = true;
            if (mHandler == null) {
                return;
            }

            mHandler.removeCallbacksAndMessages(null);
            mHandler.post(runnable);
        }
    }

    /**
     * 停止旋转
     */
    public void stop() {
        if (!mIsAutoMode) {
            pause();
        }
    }

    /**
     * 暂停旋转并消失
     */
    private void pause() {
        mIsDraw = false;
        invalidate();
        if (mHandler == null) {
            return;
        }
        mHandler.removeCallbacksAndMessages(null);
    }

    public boolean isStart() {
        return mIsDraw;
    }

    /**
     * 资源释放
     */
    public void recycle() {
        pause();
        if (pixelMapHolder != null) {
            pixelMapHolder = null;
        }
        mArcRectF = null;
        mArcPaint = null;
        mIconPaint = null;
        mGradientColors = null;
    }
}
